package com.acme.bankapp.service.bank;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.zip.DataFormatException;

import com.acme.bankapp.domain.bank.Account;
import com.acme.bankapp.domain.bank.Bank;
import com.acme.bankapp.domain.bank.CheckingAccount;
import com.acme.bankapp.domain.bank.Client;
import com.acme.bankapp.domain.bank.ClientRegistrationListener;
import com.acme.bankapp.domain.bank.Gender;
import com.acme.bankapp.domain.bank.SavingsAccount;

public class BankDataLoaderService {
  
  static private String adaptFilePathToCurrentOS(String filepath){
    return filepath.replaceAll("[\\\\/]", System.getProperty("file.separator"));
  }
  
  static private BufferedReader loadFeedFile(String filepath) throws FileNotFoundException {
    return new BufferedReader(new FileReader(adaptFilePathToCurrentOS(filepath)));
  }
  
  static private void parseFeedFileToken(String token, TreeMap<String, String> properties) throws NumberFormatException, 
      DataFormatException{
    int sep_index = token.indexOf('=');
    if(sep_index == -1){
      throw new DataFormatException();
    }
    String property = token.substring(0, sep_index);
    String prop_value = token.substring(sep_index+1, token.length());
    if(!properties.containsKey(property)){
      throw new DataFormatException();
    }
    else{
      properties.put(property, prop_value);
    }
  }
  
  static private ArrayList<Client> parseFeedFile(BufferedReader feedfile) throws IOException, DataFormatException {
    ArrayList<Client> clients = new ArrayList<Client>();
    try{
      String line;
      while((line = feedfile.readLine())!= null){
        TreeMap<String, String> properties = new TreeMap<String, String>();
        properties.put("accounttype", null);
        properties.put("balance", null);
        properties.put("overdraft", null);
        properties.put("name", null);
        properties.put("gender", null);
        StringTokenizer tokenizer = new StringTokenizer(line, ";", false);
        while(tokenizer.hasMoreTokens()){
          try{
            parseFeedFileToken(tokenizer.nextToken(), properties);
          }
          catch(NumberFormatException e){
            throw new DataFormatException(e.getMessage());
          }
        }
        for(Map.Entry<String,String> entry : properties.entrySet()) {
          if(entry.getValue() == null){
            throw new DataFormatException();
          }
        }
        
        Account account;
        switch(properties.get("accounttype")){
        case "s":
          account = new SavingsAccount(Double.parseDouble(properties.get("balance")));
          break;
        case "c":
          account = new CheckingAccount(Double.parseDouble(properties.get("balance")),Double.parseDouble(properties.get("overdraft")));
          break;
        default:
          throw new DataFormatException();
        }
        ArrayList<Account> accounts = new ArrayList<Account>();
        accounts.add(account);
        Gender gender;
        switch(properties.get("gender")){
        case "m":
          gender = Gender.MALE;
          break;
        case "f":
          gender = Gender.FEMALE;
          break;
        default:
          throw new DataFormatException();
        }
        clients.add(new Client(properties.get("name"), gender , accounts));
      }
      return clients;
    }
    finally{
      feedfile.close();
    }
  }
  
  static public Bank loadBankFromFile(String filepath) throws DataFormatException {
    Bank bank;
    try{
      bank = new Bank(new ArrayList<ClientRegistrationListener>(), parseFeedFile(loadFeedFile(adaptFilePathToCurrentOS(filepath))));
    }
    catch(IOException e){
      throw new DataFormatException(e.getMessage());
    }
    return bank;
  }

  static public void saveBank(Bank bank) throws DataFormatException {
    String sourcefilepath = "/home/p1/Рабочий стол/bank.source";
    try{
      ObjectOutputStream outputstream = new ObjectOutputStream(new FileOutputStream(adaptFilePathToCurrentOS(sourcefilepath)));
      outputstream.writeObject(bank);
      outputstream.close();
    }
    catch(IOException e){
      throw new DataFormatException(e.getMessage());
    }
  }
  
  static public Bank readBank() throws DataFormatException {
    String sourcefilepath = "/home/p1/Рабочий стол/bank.source";
    Bank bank;
    try{
      ObjectInputStream inputstream = new ObjectInputStream(new FileInputStream(adaptFilePathToCurrentOS(sourcefilepath)));
      bank = (Bank)inputstream.readObject();
      inputstream.close();
    }
    catch(IOException|ClassCastException|ClassNotFoundException e){
      throw new DataFormatException(e.getMessage());
    }
    return bank;
  }

}
